package csvchk

import (
	"testing"
)

func Benchmark_ParseValues(b *testing.B) {
	csvstruct := "int*;bool;Int2;numeric(5,2);float8;date;varchar(10)"
	csvstrs := parseStucture(csvstruct)
	for i := 0; i < b.N; i++ {
		csvstrs[0].check("123")
		csvstrs[1].check("true")
		csvstrs[2].check("12345")
		csvstrs[3].check("123.45")
		csvstrs[4].check("123.0001E+2")
		csvstrs[5].check("2018-10-27")
		csvstrs[6].check("АбвГдеAbcd")
	}
}

func Test_ParseStucture(t *testing.T) {

	csvstruct := "i*;bool;I2;numeric(5,2);float;date;v(10);uuid"

	csvstrs := parseStucture(csvstruct)

	// i*
	test(t, &csvstrs[0], "0", true, "TYPE")
	test(t, &csvstrs[0], "123", true, "TYPE")
	test(t, &csvstrs[0], "2147483647", true, "TYPE")
	test(t, &csvstrs[0], "2147483648", false, "TYPE")
	test(t, &csvstrs[0], "-2147483648", true, "TYPE")
	test(t, &csvstrs[0], "-2147483649", false, "TYPE")
	test(t, &csvstrs[0], "a123", false, "TYPE")
	test(t, &csvstrs[0], "", false, "NULL")

	// bool
	test(t, &csvstrs[1], "true", true, "TYPE")
	test(t, &csvstrs[1], "false", true, "TYPE")
	test(t, &csvstrs[1], "TRUE", true, "TYPE")
	test(t, &csvstrs[1], "FALSE", true, "TYPE")
	test(t, &csvstrs[1], "1", true, "TYPE")
	test(t, &csvstrs[1], "0", true, "TYPE")
	test(t, &csvstrs[1], "", true, "NULL")
	test(t, &csvstrs[1], "-1", false, "TYPE")

	// I2
	test(t, &csvstrs[2], "0", true, "TYPE")
	test(t, &csvstrs[2], "12345", true, "TYPE")
	test(t, &csvstrs[2], "-12345", true, "TYPE")
	test(t, &csvstrs[2], "", true, "NULL")
	test(t, &csvstrs[2], "32767", true, "TYPE")
	test(t, &csvstrs[2], "32768", false, "TYPE")
	test(t, &csvstrs[2], "-32768", true, "TYPE")
	test(t, &csvstrs[2], "-32769", false, "TYPE")
	test(t, &csvstrs[2], "a12345", false, "TYPE")

	// numeric(5,2)
	test(t, &csvstrs[3], "0", true, "TYPE")
	test(t, &csvstrs[3], "0.0001", true, "TYPE")
	test(t, &csvstrs[3], "123.45", true, "TYPE")
	test(t, &csvstrs[3], "-123.45", true, "TYPE")
	test(t, &csvstrs[3], "", true, "NULL")
	test(t, &csvstrs[3], "1000", false, "OVRF")

	// float
	test(t, &csvstrs[4], "0", true, "TYPE")
	test(t, &csvstrs[4], "123.0001E+2", true, "TYPE")
	test(t, &csvstrs[4], "123.45", true, "TYPE")
	test(t, &csvstrs[4], "-123.45", true, "TYPE")
	test(t, &csvstrs[4], "", true, "NULL")
	test(t, &csvstrs[4], "100000000000000000000000.7685686868767868", true, "TYPE")

	// date
	test(t, &csvstrs[5], "", true, "NULL")
	test(t, &csvstrs[5], "2018-08-18", true, "TYPE")
	test(t, &csvstrs[5], "2018-08-18 12:34:56", true, "TYPE")
	test(t, &csvstrs[5], "2018-08-18 12:34:56.999999", true, "TYPE")
	test(t, &csvstrs[5], "2018-08-18 12:34:56 +03:00", true, "TYPE")
	test(t, &csvstrs[5], "2018-08-18 12:34:56.999999 +03:00", true, "TYPE")
	test(t, &csvstrs[5], "18.08.2018 12:34:56 +03:00", false, "TYPE")

	// vatchar(10)
	test(t, &csvstrs[6], "", true, "NULL")
	test(t, &csvstrs[6], "qweasdzxc", true, "SIZE")
	test(t, &csvstrs[6], "qweasdzxc_-", false, "SIZE")
	test(t, &csvstrs[6], "йцуфывячс", true, "SIZE")
	test(t, &csvstrs[6], "йцуфывячс_-", false, "SIZE")

	// unknown datatype (uuid)
	test(t, &csvstrs[7], "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", true, "")

	// empty struct
	csvstrs = parseStucture("")
	equal(t, 0, len(csvstrs), "empty")
}

func test(t *testing.T, csvstr *csvstr, text string,
	expRes bool, expErr string) {
	actRes, actErr := csvstr.check(text)
	equal(t, expRes, actRes, text)
	equal(t, expErr, actErr, text)
}

func equal(t *testing.T, exp interface{}, act interface{}, val interface{}) {
	if exp != act {
		t.Errorf("'%v' <> '%v'; '%v'\n", exp, act, val)
	}
}
